from circuit import *

class TreeNode():
    def __init__(self, fa: FA, children : list, opt_target) -> None:
        self.fa : FA = fa
        self.children : list[TreeNode] = children
        self.opt_target = opt_target

    def __repr__(self) -> str:
        return f'[{self.fa.name}]'

    def is_leaf(self) -> bool:
        return self.children == None

    def input_status(self) -> None:
        return self.fa.input_status
    
    def set_input_status(self, status) -> None:
        self.fa.input_status = status
    
    def optimize(self):
        if self.opt_target == 'S':
            self.fa.S_opt = True
        else:
            self.fa.CO_opt = True

    def child(self, index):
        return self.children[index]

    def child1(self):
        return self.children[0]
    
    def child2(self):
        return self.children[1]
    
    def child3(self):
        return self.children[2]

def build_tree(fa : FA, opt_target) -> TreeNode:
    if fa.input_status != InputStatus.UNSURE:
        return TreeNode(fa, None, opt_target)

    children : list[TreeNode] = []
    for wire in fa.input_wires.values():
        circuit = wire.input_circuit
        if isinstance(circuit, FA):
            opt_t = 'S' if (circuit.output_wires['S'] is wire) else 'CO'
            children.append(build_tree(circuit, opt_t))

    return TreeNode(fa, children, opt_target)

class SimplifyTree():
    def __init__(self, root_fa : FA) -> None:
        self.root : TreeNode = build_tree(root_fa, 'S')

    def simplify(self):
        SimplifyTree.__do_simplify(self.root, True)

    def show(self):
        SimplifyTree.__do_show(self.root)
        
    def __do_simplify(root : TreeNode, is_output) -> InputStatus:
        if root.is_leaf():
            input_status = root.input_status()
            if is_output:
                if input_status == InputStatus.INVERT:
                    root.optimize()
                    return InputStatus.RIGHT
            else:
                return input_status
        else:
            child1_input_status = SimplifyTree.__do_simplify(root.child1(), False)
            child2_input_status = SimplifyTree.__do_simplify(root.child2(), False)
            child3_input_status = SimplifyTree.__do_simplify(root.child3(), False)

            if is_output:
                # case 1: t t t => Simplify child1, child2, child3 and nodes
                if   (child1_input_status == InputStatus.RIGHT) and (child2_input_status == InputStatus.RIGHT) and (child3_input_status == InputStatus.RIGHT):
                    root.child1().optimize()
                    root.child2().optimize()
                    root.child3().optimize()
                    root.optimize()
                    root.set_input_status(InputStatus.INVERT)
                # case 2: t t f => Simplify child1, child2 and nodes
                elif child1_input_status == InputStatus.RIGHT and child2_input_status == InputStatus.RIGHT and child3_input_status == InputStatus.INVERT:
                    root.child1().optimize()
                    root.child2().optimize()
                    root.optimize()
                    root.set_input_status(InputStatus.INVERT)
                # case 3: t f t => Simplify child1, child3 and nodes
                elif child1_input_status == InputStatus.RIGHT and child2_input_status == InputStatus.INVERT and child3_input_status == InputStatus.RIGHT:
                    root.child1().optimize()
                    root.child3().optimize()
                    root.optimize()
                    root.set_input_status(InputStatus.INVERT)
                # case 4: t f f => Simplify child1 and nodes
                elif child1_input_status == InputStatus.RIGHT and child2_input_status == InputStatus.INVERT and child3_input_status == InputStatus.INVERT:
                    root.child1().optimize()
                    root.optimize()
                    root.set_input_status(InputStatus.INVERT)
                # case 5: f t t => Simplify child2, child3 and nodes
                elif child1_input_status == InputStatus.INVERT and child2_input_status == InputStatus.RIGHT and child3_input_status == InputStatus.RIGHT:
                    root.child2().optimize()
                    root.child3().optimize()
                    root.optimize()
                    root.set_input_status(InputStatus.INVERT)
                # case 6: f t f => Simplify child2 and nodes
                elif child1_input_status == InputStatus.INVERT and child2_input_status == InputStatus.RIGHT and child3_input_status == InputStatus.INVERT:
                    root.child2().optimize()
                    root.optimize()
                    root.set_input_status(InputStatus.INVERT)
                # case 7: f f t => Simplify child3 and nodes
                elif child1_input_status == InputStatus.INVERT and child2_input_status == InputStatus.INVERT and child3_input_status == InputStatus.RIGHT:
                    root.child3().optimize()
                    root.optimize()
                    root.set_input_status(InputStatus.INVERT)
                # case 8: f f f => Simplify child1, child2, child3
                elif child1_input_status == InputStatus.INVERT and child2_input_status == InputStatus.INVERT and child3_input_status == InputStatus.INVERT:
                    root.child1().optimize()
                    root.child2().optimize()
                    root.child3().optimize()
                    root.set_input_status(InputStatus.RIGHT)
            else:
                # case 1: t t t => Simplify child1, child2, child3, return False
                if   (child1_input_status == InputStatus.RIGHT) and (child2_input_status == InputStatus.RIGHT) and (child3_input_status == InputStatus.RIGHT):
                    root.child1().optimize()
                    root.child2().optimize()
                    root.child3().optimize()
                    root.set_input_status(InputStatus.INVERT)
                    return InputStatus.INVERT
                # case 2: t t f => Simplify child1, child2, return False
                elif child1_input_status == InputStatus.RIGHT and child2_input_status == InputStatus.RIGHT and child3_input_status == InputStatus.INVERT:
                    root.child1().optimize()
                    root.child2().optimize()
                    root.set_input_status(InputStatus.INVERT)
                    return InputStatus.INVERT
                # case 3: t f t => Simplify child1, child3, return False
                elif child1_input_status == InputStatus.RIGHT and child2_input_status == InputStatus.INVERT and child3_input_status == InputStatus.RIGHT:
                    root.child1().optimize()
                    root.child3().optimize()
                    root.set_input_status(InputStatus.INVERT)
                    return InputStatus.INVERT
                # case 4: t f f => Simplify child2, child3, return InputStatus.RIGHT 
                elif (child1_input_status == InputStatus.RIGHT) and (child2_input_status == InputStatus.INVERT) and (child3_input_status == InputStatus.INVERT):
                    root.child2().optimize()
                    root.child3().optimize()
                    root.set_input_status(InputStatus.RIGHT)
                    return InputStatus.RIGHT
                # case 5: f t t => Simplify child2, child3, return False
                elif child1_input_status == InputStatus.INVERT and child2_input_status == InputStatus.RIGHT and child3_input_status == InputStatus.RIGHT:
                    root.child2().optimize()
                    root.child3().optimize()
                    root.set_input_status(InputStatus.INVERT)
                    return InputStatus.INVERT
                # case 6: f t f => Simplify child1, child3, return True
                elif child1_input_status == InputStatus.INVERT and child2_input_status == InputStatus.RIGHT and child3_input_status == InputStatus.INVERT:
                    root.child1().optimize()
                    root.child3().optimize()
                    root.set_input_status(InputStatus.RIGHT)
                    return InputStatus.RIGHT
                # case 7: f f t => Simplify child1, child2, return True
                elif child1_input_status == InputStatus.INVERT and child2_input_status == InputStatus.INVERT and child3_input_status == InputStatus.RIGHT:
                    root.child1().optimize()
                    root.child2().optimize()
                    root.set_input_status(InputStatus.RIGHT)
                    return InputStatus.RIGHT
                # case 8: f f f => Simplify child1, child2, child3, return True
                elif child1_input_status == InputStatus.INVERT and child2_input_status == InputStatus.INVERT and child3_input_status == InputStatus.INVERT:
                    root.child1().optimize()
                    root.child2().optimize()
                    root.child3().optimize()
                    root.set_input_status(InputStatus.RIGHT)
                    return InputStatus.RIGHT
    
    def __do_show(node : TreeNode, colum = 0):
        print('\r', end='')
        print(f'\033[{colum}C', end='')
        print(node)
        next_colum = colum + 2 + len(node.fa.name)
        if  node.is_leaf():
            return
        else:
            for child in node.children:
                SimplifyTree.__do_show(child, next_colum)